package ansible

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"time"

	"github.com/creack/pty"
)

// ExecutionResult 表示命令执行结果
type ExecutionResult struct {
	Stdout   []byte        // 标准输出
	Stderr   []byte        // 标准错误输出
	ExitCode int           // 退出码
	Duration time.Duration // 执行时间
	Command  string        // 执行的命令
	Args     []string      // 命令参数
}

// Success 返回命令是否执行成功
func (r *ExecutionResult) Success() bool {
	return r.ExitCode == 0
}

// Executor 定义命令执行器接口
type Executor interface {
	Execute(ctx context.Context, config *Config, args []string) (*ExecutionResult, error)
}

// DefaultExecutor 默认的命令执行器实现
type DefaultExecutor struct {
	stdout io.Writer
	stderr io.Writer
	stdin  io.Reader
}

// NewDefaultExecutor 创建默认执行器
func NewDefaultExecutor() *DefaultExecutor {
	return &DefaultExecutor{}
}

// WithStdout 设置标准输出
func (e *DefaultExecutor) WithStdout(w io.Writer) *DefaultExecutor {
	e.stdout = w
	return e
}

// WithStderr 设置标准错误输出
func (e *DefaultExecutor) WithStderr(w io.Writer) *DefaultExecutor {
	e.stderr = w
	return e
}

// WithStdin 设置标准输入
func (e *DefaultExecutor) WithStdin(r io.Reader) *DefaultExecutor {
	e.stdin = r
	return e
}

// Execute 执行命令
func (e *DefaultExecutor) Execute(ctx context.Context, config *Config, args []string) (*ExecutionResult, error) {
	startTime := time.Now()
	
	// 创建带超时的上下文
	if config.Timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, config.Timeout)
		defer cancel()
	}
	
	// 创建命令
	cmd := exec.CommandContext(ctx, config.BinPath, args...)
	if config.WorkDirectory != "" {
		cmd.Dir = config.WorkDirectory
	}
	
	// 设置环境变量
	envs := config.GetAnsibleEnvs()
	for k, v := range envs {
		cmd.Env = append(cmd.Environ(), fmt.Sprintf("%s=%s", k, v))
	}
	
	// 记录命令执行
	config.Logger.LogCommand(ctx, config.BinPath, args)
	
	var result *ExecutionResult
	var err error
	
	if config.EnablePTY {
		result, err = e.executeWithPTY(ctx, cmd, config)
	} else {
		result, err = e.executeNormal(ctx, cmd, config)
	}
	
	if result != nil {
		result.Duration = time.Since(startTime)
		result.Command = config.BinPath
		result.Args = args
		
		// 记录执行结果
		config.Logger.LogCommandResult(ctx, config.BinPath, result.Success(), result.Duration.String(), err)
	}
	
	return result, err
}

// executeNormal 正常模式执行命令
func (e *DefaultExecutor) executeNormal(ctx context.Context, cmd *exec.Cmd, config *Config) (*ExecutionResult, error) {
	var stdout, stderr bytes.Buffer
	
	// 设置输出
	if e.stdout != nil {
		cmd.Stdout = e.stdout
	} else {
		cmd.Stdout = &stdout
	}
	
	if e.stderr != nil {
		cmd.Stderr = e.stderr
	} else {
		cmd.Stderr = &stderr
	}
	
	if e.stdin != nil {
		cmd.Stdin = e.stdin
	}
	
	// 启动命令
	err := cmd.Start()
	if err != nil {
		return &ExecutionResult{
			Stdout:   stdout.Bytes(),
			Stderr:   stderr.Bytes(),
			ExitCode: -1,
		}, NewAnsibleError("start", cmd.String(), err, stdout.Bytes(), stderr.Bytes())
	}
	
	// 等待命令完成
	err = cmd.Wait()
	exitCode := 0
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			exitCode = exitError.ExitCode()
		} else {
			exitCode = -1
		}
	}
	
	result := &ExecutionResult{
		Stdout:   stdout.Bytes(),
		Stderr:   stderr.Bytes(),
		ExitCode: exitCode,
	}
	
	if err != nil && exitCode != 0 {
		return result, NewAnsibleError("execute", cmd.String(), err, stdout.Bytes(), stderr.Bytes())
	}
	
	return result, nil
}

// executeWithPTY 使用 PTY 执行命令
func (e *DefaultExecutor) executeWithPTY(ctx context.Context, cmd *exec.Cmd, config *Config) (*ExecutionResult, error) {
	var stdout, stderr bytes.Buffer
	
	// 启动 PTY
	ptmx, err := pty.Start(cmd)
	if err != nil {
		return &ExecutionResult{
			Stdout:   stdout.Bytes(),
			Stderr:   stderr.Bytes(),
			ExitCode: -1,
		}, NewAnsibleError("pty_start", cmd.String(), err, stdout.Bytes(), stderr.Bytes())
	}
	defer ptmx.Close()
	
	// 处理输出
	if e.stdout != nil {
		_, err = io.Copy(e.stdout, ptmx)
	} else {
		_, err = io.Copy(&stdout, ptmx)
	}
	
	if err != nil {
		return &ExecutionResult{
			Stdout:   stdout.Bytes(),
			Stderr:   stderr.Bytes(),
			ExitCode: -1,
		}, NewAnsibleError("pty_copy", cmd.String(), err, stdout.Bytes(), stderr.Bytes())
	}
	
	// 等待命令完成
	err = cmd.Wait()
	exitCode := 0
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			exitCode = exitError.ExitCode()
		} else {
			exitCode = -1
		}
	}
	
	result := &ExecutionResult{
		Stdout:   stdout.Bytes(),
		Stderr:   stderr.Bytes(),
		ExitCode: exitCode,
	}
	
	if err != nil && exitCode != 0 {
		return result, NewAnsibleError("pty_execute", cmd.String(), err, stdout.Bytes(), stderr.Bytes())
	}
	
	return result, nil
}

// BuildCommand 构建命令参数
func BuildCommand(inventory interface{}, hosts []string, module string, moduleArgs []string, extraVars map[string]string) ([]string, error) {
	var args []string
	
	// 处理 inventory
	switch inv := inventory.(type) {
	case string:
		if inv != "" {
			args = append(args, "-i", inv)
		}
	case *Inventory:
		tempFile, err := inv.LocalStorage()
		if err != nil {
			return nil, fmt.Errorf("failed to create inventory file: %w", err)
		}
		args = append(args, "-i", tempFile)
	case nil:
		// 使用默认 inventory
	default:
		return nil, ErrInvalidInventory
	}
	
	// 添加主机列表
	if len(hosts) > 0 {
		args = append(args, strings.Join(hosts, ","))
	}
	
	// 添加模块
	if module == "" {
		return nil, ErrEmptyModule
	}
	args = append(args, "-m", module)
	
	// 添加模块参数
	if len(moduleArgs) > 0 {
		args = append(args, "-a")
		args = append(args, moduleArgs...)
	}
	
	// 添加额外变量
	for k, v := range extraVars {
		args = append(args, "-e", fmt.Sprintf("%s=%s", k, v))
	}
	
	return args, nil
}
